//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2013 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#ifndef _BUOLA_FUNCTORS_PREDICATES_BINARY_H_
#define _BUOLA_FUNCTORS_PREDICATES_BINARY_H_

#include <buola/buola.h>

namespace buola { namespace fn {

///\addtogroup functors
///@{

///\name Comparison (binary) predicates
//@{

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///
/// tag class for binary functions which can be used as predicates
///
/// This contains several function classes which can be used where a binary predicate is needed. Among others, it
/// contains functions to compare values with each other, and also to do it in c case-insensitive way.
///
/// Most of the ones that already appear in the standarad library also appear here so that they can work with
/// arguments of different types, as long as operator== is defined
///
/// It also contains boolean operators to combine the functions together.
///
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
struct GBinaryPredicate {};
    
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///
/// tag class for binary functions which can be used as a sorting comparison function
///
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
struct GOrderPredicate : public GBinaryPredicate {};
    
/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// equal binary predicate
///
/// Evaluated with two arguments \p p1 and \p p2, returns true if \c p1==p2
///
/////////////////////////////////////////////////////////////////////////////////////////////////
struct AEqual : public GBinaryPredicate
{
    ///returns \c true if \p p1 is equal to \p p2
    template<typename t1,typename t2>
    bool operator()(const t1 &p1,const t2 &p2) const
    {
        return p1==p2;
    }
};

/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// not-equal binary predicate
///
/// Evaluated with two arguments \p p1 and \p p2, returns true if \c p1!=p2
///
/////////////////////////////////////////////////////////////////////////////////////////////////
struct ANotEqual : public GBinaryPredicate
{
    ///returns \c true if \p p1 is not equal to \p p2
    template<typename t1,typename t2>
    bool operator()(const t1 &p1,const t2 &p2) const
    {
        return p1!=p2;
    }
};

/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// less binary predicate
///
/// Evaluated with two arguments \p p1 and \p p2, returns true if \c p1<p2
///
/////////////////////////////////////////////////////////////////////////////////////////////////
struct ALess : public GOrderPredicate
{
    ///returns \c true if \p p1 is less than \p p2
    template<typename t1,typename t2>
    bool operator()(const t1 &p1,const t2 &p2) const
    {
        return p1<p2;
    }
};

/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// greater binary predicate
///
/// Evaluated with two arguments \p p1 and \p p2, returns true if \c p1>p2
///
/////////////////////////////////////////////////////////////////////////////////////////////////
struct AGreater : public GOrderPredicate
{
    ///returns \c true if \p p1 is greater than \p p2
    template<typename t1,typename t2>
    bool operator()(const t1 &p1,const t2 &p2) const
    {
        return p1>p2;
    }
};

/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// less-or-equal binary predicate
///
/// Evaluated with two arguments \p p1 and \p p2, returns true if \c p1<=p2
///
/////////////////////////////////////////////////////////////////////////////////////////////////
struct ALessEqual : public GOrderPredicate
{
    ///returns \c true if \p p1 is less or equal than \p p2
    template<typename t1,typename t2>
    bool operator()(const t1 &p1,const t2 &p2) const
    {
        return p1<=p2;
    }
};

/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// greater-or-equal binary predicate
///
/// Evaluated with two arguments \p p1 and \p p2, returns true if \c p1>p2
///
/////////////////////////////////////////////////////////////////////////////////////////////////
struct AGreaterEqual : public GOrderPredicate
{
    ///returns \c true if \p p1 is greater or equal than \p p2
    template<typename t1,typename t2>
    bool operator()(const t1 &p1,const t2 &p2) const
    {
        return p1>=p2;
    }
};

/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// binary predicate OR
///
/// Combines two binary predicates into a single predicate, which is a logical \e OR of its two
/// arguments
///
/////////////////////////////////////////////////////////////////////////////////////////////////
template<typename tLH,typename tRH>
struct ABinaryPredicateOr : public GBinaryPredicate
{
    ///initializes the sub-predicates from the arguments
    template<typename tALH,typename tARH>
    ABinaryPredicateOr(tALH &&pLH,tARH &&pRH)
        :   mLH(std::forward<tALH>(pLH))
        ,   mRH(std::forward<tARH>(pRH))
    {}
    
    ///returns \e true if either of the sub-predicates returns \e true for \p p1 and \p p2
    template<typename t1,typename t2>
    bool operator()(const t1 &p1,const t2 &p2) const
    {
        return mLH(p1,p2)||mRH(p1,p2);
    }
    
private:
    tLH mLH;
    tRH mRH;
};

/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// binary predicate AND
///
/// Combines two binary predicates into a single predicate, which is a logical \e AND of its two
/// arguments
///
/////////////////////////////////////////////////////////////////////////////////////////////////
template<typename tLH,typename tRH>
struct ABinaryPredicateAnd : public GBinaryPredicate
{
    ///initializes the sub-predicates from the arguments
    template<typename tALH,typename tARH>
    ABinaryPredicateAnd(tALH &&pLH,tARH &&pRH)
        :   mLH(std::forward<tALH>(pLH))
        ,   mRH(std::forward<tARH>(pRH))
    {}
    
    ///returns \e true if both sub-predicates return \e true for \p p1 and \p p2
    template<typename t1,typename t2>
    bool operator()(const t1 &p1,const t2 &p2) const
    {
        return mLH(p1,p2)&&mRH(p1,p2);
    }
    
private:
    tLH mLH;
    tRH mRH;
};

/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// binary predicate NOT
///
/// Creates a new binary predicate, which is a logical \e NOT of its argument
///
/////////////////////////////////////////////////////////////////////////////////////////////////
template<typename tSub>
struct ABinaryPredicateNot : public GBinaryPredicate
{
    ///initializes the sub-predicate from the argument
    explicit ABinaryPredicateNot(const tSub &pSub)
        :   mSub(pSub)
    {}
    
    ///initializes the sub-predicate from the argument (move version)
    explicit ABinaryPredicateNot(tSub &&pSub)
        :   mSub(std::move(pSub))
    {}
    
    ///returns \e true if the sub-predicate returns \e false for \p p1 and \p p2
    template<typename t1,typename t2>
    bool operator()(const t1 &p1,const t2 &p2) const
    {
        return !mSub(p1,p2);
    }
    
private:
    tSub mSub;
};

/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// dereferencing binary predicate
///
/// Creates a new binary predicate, which dereferences its argument before passing it down
/// to the sub-predicate
///
/////////////////////////////////////////////////////////////////////////////////////////////////
template<typename tSub>
struct ABinaryPredicateIndirect : public GBinaryPredicate
{
    ///initializes the sub-predicate from the arguments
    explicit ABinaryPredicateIndirect(const tSub &pSub)
        :   mSub(pSub)
    {}
    
    ///initializes the sub-predicate from the arguments (move version)
    explicit ABinaryPredicateIndirect(tSub &&pSub)
        :   mSub(std::move(pSub))
    {}
    
    ///returns \e true if the sub-predicate returns \e true for \p *p1 and \p *p2
    template<typename t1,typename t2>
    bool operator()(const t1 &p1,const t2 &p2) const
    {
        return mSub(*p1,*p2);
    }
    
private:
    tSub mSub;
};

///returns a predicate which is a logical \e OR of the arguments
template<typename tLH,typename tRH>
inline OEnableIfTag2<tLH,tRH,GBinaryPredicate,ABinaryPredicateOr<ODecay<tLH>,ODecay<tRH>>>
operator||(tLH &&pLH,tRH &&pRH)
{
    return {std::forward<tLH>(pLH),std::forward<tRH>(pRH)};
}

///returns a predicate which is a logical \e AND of the arguments
template<typename tLH,typename tRH>
inline OEnableIfTag2<tLH,tRH,GBinaryPredicate,ABinaryPredicateAnd<ODecay<tLH>,ODecay<tRH>>>
operator&&(tLH &&pLH,tRH &&pRH)
{
    return {std::forward<tLH>(pLH),std::forward<tRH>(pRH)};
}

///returns a predicate which is a logical \e NOT of the argument
template<typename tSub>
inline OEnableIfTag<tSub,GBinaryPredicate,ABinaryPredicateNot<ODecay<tSub>>>
operator!(tSub &&pSub)
{
    return ABinaryPredicateNot<ODecay<tSub>>(std::forward<tSub>(pSub));
}

///returns a predicate which dereferences its arguments before passing it to \p pSub
template<typename tSub>
inline OEnableIfTag<tSub,GBinaryPredicate,ABinaryPredicateIndirect<ODecay<tSub>>> indirect(tSub &&pSub)
{
    return ABinaryPredicateIndirect<ODecay<tSub>>(std::forward<tSub>(pSub));
}

/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// order predicate combiner
///
/// Combines two binary predicates into a single predicate, so that the second one is used if the 
/// first one evaluates to false in both directions
///
/////////////////////////////////////////////////////////////////////////////////////////////////
template<typename tLH,typename tRH>
struct AOrderCombiner : public GOrderPredicate
{
    ///initializes the sub-predicates from the arguments
    template<typename tALH,typename tARH>
    AOrderCombiner(tALH &&pLH,tARH &&pRH)
        :   mLH(std::forward<tALH>(pLH))
        ,   mRH(std::forward<tARH>(pRH))
    {}
    
    ///returns \e true if either of the sub-predicates returns \e true for \p p1 and \p p2
    template<typename t1,typename t2>
    bool operator()(const t1 &p1,const t2 &p2) const
    {
        if(mLH(p1,p2))
            return true;
        if(mLH(p2,p1))
            return false;
        return mRH(p1,p2);
    }
    
private:
    tLH mLH;
    tRH mRH;
};

///returns a predicate which is a logical \e OR of the arguments
template<typename tLH,typename tRH>
inline OEnableIfTag2<tLH,tRH,GOrderPredicate,AOrderCombiner<ODecay<tLH>,ODecay<tRH>>>
operator>>(tLH &&pLH,tRH &&pRH)
{
    return AOrderCombiner<ODecay<tLH>,ODecay<tRH>>(std::forward<tLH>(pLH),std::forward<tRH>(pRH));
}

///returns a predicate which returns true if its argument are equal
inline AEqual equal()
{
    return AEqual();
}

///returns a predicate which returns true if its argument are different
inline ANotEqual not_equal()
{
    return ANotEqual();
}

///returns a predicate which returns true if its first argument is less than the second
inline ALess less()
{
    return ALess();
}

///returns a predicate which returns true if its first argument is greater than the second
inline AGreater greater()
{
    return AGreater();
}

///returns a predicate which returns true if its first argument is less or equal than the second
inline ALessEqual less_equal()
{
    return ALessEqual();
}

///returns a predicate which returns true if its first argument is greater or equal than the second
inline AGreaterEqual greater_equal()
{
    return AGreaterEqual();
}

//@}

///@}

/*namespace fn*/ } /*namespace buola*/ }

#endif
